<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>防抖和节流</title>
</head>
<body>
    <h1>防抖:Debounced Search Input</h1>
    <input type="text" id="search-input" placeholder="Type to search...">
    <p id="output"></p>

    <h1>节流:Button Click</h1>
    <button id="throttle-button">点击我</button>
    <p id="output2"></p>
    <script>
        /**
         * 节流: 节约重复动作,n秒内只运行一次，n秒内重复触发，只有一次运行--不要打断它,比方说高频事件(快速点击，鼠标滚动,resize事件,scroll事件)、下拉加载，视频播放记录时间
         * 
         * 防抖: n秒后再执行该事件，若n秒内被重复触发，则重新计时-只执行最后一次，1、比如搜索框搜索输入, 2、文本编辑器实时保存 
         * **/ 

        // 防抖函数
        function debounce(fn, delay) {
            let timerId = null; // 用于存储 setTimeout 返回的定时器 ID
            return function(...args) { // 返回一个新的函数，这个函数将被实际调用
                // 清除之前设置的定时器
                if (timerId) {
                    clearTimeout(timerId); // 如果之前有定时器正在运行，则清除它
                }
                // 设置新的定时器
                timerId = setTimeout(() => {
                    fn.apply(this, args); // 当定时器到期时，执行原始函数 fn，并传递正确的 this 上下文和参数
                }, delay); // 延迟时间为 delay 毫秒
            };
        }

        document.addEventListener('DOMContentLoaded', () => {
            const searchInput = document.getElementById('search-input');
            const output = document.getElementById('output');

            // 模拟搜索函数
            function search(query) {
                console.log(`Search query: ${query}`);
                output.textContent = `输入的是: "${query}"`;
            }

            // 使用防抖包装搜索函数
            const debouncedSearch = debounce(function(event) {
                search(event.target.value);
            }, 2000); // 设置防抖时间为1000毫秒（1秒）

            // 绑定输入事件监听器
            searchInput.addEventListener('input', debouncedSearch);
        });

        // 节流函数--throttle有两种写法，1、时间戳 2、定时器,它俩的区别在于时间戳会立即执行，定时器要延迟一点点，总体来说定时器体验感好点
        function throttle(fn, delay) {
            let lastCall = 0;
            return function(...args) {
                const now = new Date().getTime();
                if (now - lastCall >= delay) {
                    lastCall = now;
                    fn.apply(this, args);
                }
            };
            
        }
        function throttled2(fn, delay = 500) {
            let timer = null; // 用于存储 setTimeout 返回的定时器 ID
            return function (...args) { // 返回一个新的函数，这个函数将被实际调用
                if (!timer) { // 如果当前没有定时器正在运行
                    timer = setTimeout(() => { // 设置一个新的定时器
                        fn.apply(this, args); // 当定时器到期时，执行原始函数 fn，并传递正确的 this 上下文和参数
                        timer = null; // 执行完后，清除定时器引用，允许下一次触发
                    }, delay); // 延迟时间为 delay 毫秒（默认为 500）
                }
            };
        }

        document.addEventListener('DOMContentLoaded', () => {
            const button = document.getElementById('throttle-button');
            const output2 = document.getElementById('output2');

            // 模拟点击处理函数
            function handleClick() {
                console.log('点击了');
                output2.textContent = '按钮被点击了';
            }

            // 使用节流包装点击处理函数
            // const throttledClick = throttle(handleClick, 2000); // 设置节流时间为2000毫秒（2秒）
            const throttledClick = throttled2(handleClick, 2000); // 设置节流时间为2000毫秒（2秒）

            // 绑定点击事件监听器
            button.addEventListener('click', throttledClick);
        });
    </script>
</body>
</html>